/*
 * Copyright 2008 the Breachwalls OSS project and the Others.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
 * either express or implied. See the License for the specific language
 * governing permissions and limitations under the License.
 */
package com.breachwalls.mogen.xml.context;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;

import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.breachwalls.mogen.model.GenericModel;
import com.breachwalls.mogen.model.Model;
import com.breachwalls.mogen.model.ModelProperty;
import com.breachwalls.mogen.model.ModelType;
import com.breachwalls.mogen.xml.bean.GenerateModels;
import com.breachwalls.mogen.xml.bean.ModelClass;

/**
 * 自動生成ファイルを読み込んだモデルの情報を保存しておくためのコンテキストのデフォルト実装.
 * @author todoken
 * @version $Revision: 31 $
 */
public class DefaultModelParseContext implements ModelParseContext {

    /**
     * ロガー.
     */
    private static Logger logger = LoggerFactory.getLogger(DefaultModelParseContext.class);

    /**
     * モデルを保存するマップストレージ.
     */
    private Map<String, Model> modelMap = new HashMap<String, Model>();

    /**
     * {@inheritDoc}
     */
    public void register(final GenerateModels models) {
        for (ModelClass m : models.getModelClasses()) {
            final String mode = m.getMode();
            if (StringUtils.isBlank(mode)) {
                m.setMode(models.getMode());
            }

            Model model = new GenericModel(m);
            this.modelMap.put(model.getName(), model);
        }
    }

    /**
     * {@inheritDoc}
     */
    public List<Model> getAllModels() {
        List<Model> list = new ArrayList<Model>();
        for (Entry<String, Model> entry : modelMap.entrySet()) {
            list.add(entry.getValue());
        }
        return list;
    }

    /**
     * {@inheritDoc}
     */
    public Model findByName(final String name) {
        Model m = null;
        if (StringUtils.isNotEmpty(name)) {
            if (modelMap.containsKey(name)) {
                m = modelMap.get(name);
            } else {
                if (logger.isDebugEnabled()) {
                    logger.debug("Model [" + name + "] not found.");
                }
            }
        }
        return m;
    }

    /**
     * 全てのモデルを親モデルと関連付ける.
     */
    protected void associateParent() {
        Set<Entry<String, Model>> entries = modelMap.entrySet();
        for (Entry<String, Model> entry : entries) {
            final Model model = entry.getValue();
            model.setParent(findByName(model.getExtendsModelName()));
        }
    }

    /**
     * モデルのプロパティを他のモデルと関連付ける.
     */
    protected void associateProperty() {
        Set<Entry<String, Model>> entries = modelMap.entrySet();

        // JavaBeans のプロパティの関連付け
        for (Entry<String, Model> entry : entries) {
            final Model model = entry.getValue();

            // エンティティとコンプレックスは関連プロパティをモデルに変換する
            final List<ModelProperty> props = model.getModelProperties();
            if (model.getModelType() == ModelType.ENTITY || model.getModelType() == ModelType.DOMAIN) {
                for (ModelProperty prop : props) {
                    Model m = findByName(prop.getType());
                    if (m == null) {
                        // TODO オブジェクト型やプリミティブ型もサポートするようにしたい
                        if (logger.isDebugEnabled()) {
                            logger.debug("not found model property.");
                        }
                        // FIXME とりあえず、今はエラー
                        throw new RuntimeException("Do not define Model Property [" + prop.getName() + "]");
                    } else {
                        prop.setModel(m);
                        if (logger.isDebugEnabled()) {
                            logger.debug("found model property.");
                        }
                    }
                }
            }
        }
    }

    /**
     * {@inheritDoc}
     */
    public void compile() {
        associateParent();
        associateProperty();
    }

}
